/*
 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.naming.cosnaming;

import org.omg.CosNaming.NamingContextExtPackage.*;
import java.io.StringWriter;

// Import general CORBA classes
import org.omg.CORBA.SystemException;
import org.omg.CORBA.Object;

// Import org.omg.CosNaming types
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;


/**
 * Class InteroperableNamingImpl implements the methods defined
 * for NamingContextExt which is part of Interoperable Naming
 * Service specifications. This class is added for doing more
 * of Parsing and Building of Stringified names according to INS
 * Spec.
 */
public class InterOperableNamingImpl {

  /**
   * Method which stringifies the Name Components given as the input
   * parameter.
   *
   * @param n Array of Name Components (Simple or Compound Names)
   * @return string which is the stringified reference.
   */
  public String convertToString(org.omg.CosNaming.NameComponent[]
      theNameComponents) {
    String theConvertedString =
        convertNameComponentToString(theNameComponents[0]);
    String temp;
    for (int i = 1; i < theNameComponents.length; i++) {
      temp = convertNameComponentToString(theNameComponents[i]);
      if (temp != null) {
        theConvertedString =
            theConvertedString + "/" + convertNameComponentToString(
                theNameComponents[i]);
      }
    }
    return theConvertedString;
  }

  /**
   * This method converts a single Namecomponent to String, By adding Escapes
   * If neccessary.
   */
  private String convertNameComponentToString(
      org.omg.CosNaming.NameComponent theNameComponent) {
    if (((theNameComponent.id == null)
        || (theNameComponent.id.length() == 0))
        && ((theNameComponent.kind == null)
        || (theNameComponent.kind.length() == 0))) {
      return ".";
    } else if ((theNameComponent.id == null)
        || (theNameComponent.id.length() == 0)) {
      String kind = addEscape(theNameComponent.kind);
      return "." + kind;
    } else if ((theNameComponent.kind == null)
        || (theNameComponent.kind.length() == 0)) {
      String id = addEscape(theNameComponent.id);
      return id;
    } else {
      String id = addEscape(theNameComponent.id);
      String kind = addEscape(theNameComponent.kind);
      return (id + "." + kind);
    }
  }


  /**
   * This method adds escape '\' for the Namecomponent if neccessary
   */
  private String addEscape(String value) {
    StringBuffer theNewValue;
    if ((value != null) && ((value.indexOf('.') != -1) ||
        (value.indexOf('/') != -1))) {
      char c;
      theNewValue = new StringBuffer();
      for (int i = 0; i < value.length(); i++) {
        c = value.charAt(i);
        if ((c != '.') && (c != '/')) {
          theNewValue.append(c);
        } else {
          // Adding escape for the "."
          theNewValue.append('\\');
          theNewValue.append(c);
        }
      }
    } else {
      return value;
    }
    return new String(theNewValue);
  }

  /**
   * Method which converts the Stringified name into Array of Name Components.
   *
   * @param string which is the stringified name.
   * @return Array of Name Components (Simple or Compound Names)
   */
  public org.omg.CosNaming.NameComponent[] convertToNameComponent(
      String theStringifiedName)
      throws org.omg.CosNaming.NamingContextPackage.InvalidName {
    String[] theStringifiedNameComponents =
        breakStringToNameComponents(theStringifiedName);
    if ((theStringifiedNameComponents == null)
        || (theStringifiedNameComponents.length == 0)) {
      return null;
    }
    NameComponent[] theNameComponents =
        new NameComponent[theStringifiedNameComponents.length];
    for (int i = 0; i < theStringifiedNameComponents.length; i++) {
      theNameComponents[i] = createNameComponentFromString(
          theStringifiedNameComponents[i]);
    }
    return theNameComponents;
  }

  /**
   * Step1 in converting Stringified name into  array of Name Component
   * is breaking the String into multiple name components
   */
  private String[] breakStringToNameComponents(String theStringifiedName) {
    int[] theIndices = new int[100];
    int theIndicesIndex = 0;

    for (int index = 0; index <= theStringifiedName.length(); ) {
      theIndices[theIndicesIndex] = theStringifiedName.indexOf('/',
          index);
      if (theIndices[theIndicesIndex] == -1) {
        // This is the end of all the occurence of '/' and hence come
        // out of the loop
        index = theStringifiedName.length() + 1;
      } else {
        // If the '/' is found, first check whether it is
        // preceded by escape '\'
        // If not then set theIndices and increment theIndicesIndex
        // and also set the index else just ignore the '/'
        if ((theIndices[theIndicesIndex] > 0)
            && (theStringifiedName.charAt(
            theIndices[theIndicesIndex] - 1) == '\\')) {
          index = theIndices[theIndicesIndex] + 1;
          theIndices[theIndicesIndex] = -1;
        } else {
          index = theIndices[theIndicesIndex] + 1;
          theIndicesIndex++;
        }
      }
    }
    if (theIndicesIndex == 0) {
      String[] tempString = new String[1];
      tempString[0] = theStringifiedName;
      return tempString;
    }
    if (theIndicesIndex != 0) {
      theIndicesIndex++;
    }
    return StringComponentsFromIndices(theIndices, theIndicesIndex,
        theStringifiedName);
  }

  /**
   * This method breaks one big String into multiple substrings based
   * on the array of index passed in.
   */
  private String[] StringComponentsFromIndices(int[] theIndices,
      int indicesCount, String theStringifiedName) {
    String[] theStringComponents = new String[indicesCount];
    int firstIndex = 0;
    int lastIndex = theIndices[0];
    for (int i = 0; i < indicesCount; i++) {
      theStringComponents[i] = theStringifiedName.substring(firstIndex,
          lastIndex);
      if ((theIndices[i] < theStringifiedName.length() - 1)
          && (theIndices[i] != -1)) {
        firstIndex = theIndices[i] + 1;
      } else {
        firstIndex = 0;
        i = indicesCount;
      }
      if ((i + 1 < theIndices.length)
          && (theIndices[i + 1] < (theStringifiedName.length() - 1))
          && (theIndices[i + 1] != -1)) {
        lastIndex = theIndices[i + 1];
      } else {
        i = indicesCount;
      }
      // This is done for the last component
      if (firstIndex != 0 && i == indicesCount) {
        theStringComponents[indicesCount - 1] =
            theStringifiedName.substring(firstIndex);
      }
    }
    return theStringComponents;
  }

  /**
   * Step 2: After Breaking the Stringified name into set of NameComponent
   * Strings, The next step is to create Namecomponents from the substring
   * by removing the escapes if there are any.
   */
  private NameComponent createNameComponentFromString(
      String theStringifiedNameComponent)
      throws org.omg.CosNaming.NamingContextPackage.InvalidName

  {
    String id = null;
    String kind = null;
    if ((theStringifiedNameComponent == null)
        || (theStringifiedNameComponent.length() == 0)
        || (theStringifiedNameComponent.endsWith("."))) {
      // If any of the above is true, then we create an invalid Name
      // Component to indicate that it is an invalid name.
      throw new org.omg.CosNaming.NamingContextPackage.InvalidName();
    }

    int index = theStringifiedNameComponent.indexOf('.', 0);
    // The format could be XYZ (Without kind)
    if (index == -1) {
      id = theStringifiedNameComponent;
    }
    // The format is .XYZ (Without ID)
    else if (index == 0) {
      // This check is for the Namecomponent which is just "." meaning Id
      // and Kinds are null
      if (theStringifiedNameComponent.length() != 1) {
        kind = theStringifiedNameComponent.substring(1);
      }
    } else {
      if (theStringifiedNameComponent.charAt(index - 1) != '\\') {
        id = theStringifiedNameComponent.substring(0, index);
        kind = theStringifiedNameComponent.substring(index + 1);
      } else {
        boolean kindfound = false;
        while ((index < theStringifiedNameComponent.length())
            && (kindfound != true)) {
          index = theStringifiedNameComponent.indexOf('.', index + 1);
          if (index > 0) {
            if (theStringifiedNameComponent.charAt(
                index - 1) != '\\') {
              kindfound = true;
            }
          } else {
            // No more '.', which means there is no Kind
            index = theStringifiedNameComponent.length();
          }
        }
        if (kindfound == true) {
          id = theStringifiedNameComponent.substring(0, index);
          kind = theStringifiedNameComponent.substring(index + 1);
        } else {
          id = theStringifiedNameComponent;
        }
      }
    }
    id = cleanEscapeCharacter(id);
    kind = cleanEscapeCharacter(kind);
    if (id == null) {
      id = "";
    }
    if (kind == null) {
      kind = "";
    }
    return new NameComponent(id, kind);
  }


  /**
   * This method cleans the escapes in the Stringified name and returns the
   * correct String
   */
  private String cleanEscapeCharacter(String theString) {
    if ((theString == null) || (theString.length() == 0)) {
      return theString;
    }
    int index = theString.indexOf('\\');
    if (index == 0) {
      return theString;
    } else {
      StringBuffer src = new StringBuffer(theString);
      StringBuffer dest = new StringBuffer();
      char c;
      for (int i = 0; i < theString.length(); i++) {
        c = src.charAt(i);
        if (c != '\\') {
          dest.append(c);
        } else {
          if (i + 1 < theString.length()) {
            char d = src.charAt(i + 1);
            // If there is a AlphaNumeric character after a \
            // then include slash, as it is not intended as an
            // escape character.
            if (Character.isLetterOrDigit(d)) {
              dest.append(c);
            }
          }
        }
      }
      return new String(dest);
    }
  }

  /**
   * Method which converts the Stringified name  and Host Name Address into
   * a URL based Name
   *
   * @param address which is ip based host name
   * @param name which is the stringified name.
   * @return url based Name.
   */
  public String createURLBasedAddress(String address, String name)
      throws InvalidAddress {
    String theurl = null;
    if ((address == null)
        || (address.length() == 0)) {
      throw new InvalidAddress();
    } else {
      theurl = "corbaname:" + address + "#" + encode(name);
    }
    return theurl;
  }

  /**
   * Encodes the string according to RFC 2396 IETF spec required by INS.
   */
  private String encode(String stringToEncode) {
    StringWriter theStringAfterEscape = new StringWriter();
    int byteCount = 0;
    for (int i = 0; i < stringToEncode.length(); i++) {
      char c = stringToEncode.charAt(i);
      if (Character.isLetterOrDigit(c)) {
        theStringAfterEscape.write(c);
      }
      // Do no Escape for characters in this list
      // RFC 2396
      else if ((c == ';') || (c == '/') || (c == '?')
          || (c == ':') || (c == '@') || (c == '&') || (c == '=')
          || (c == '+') || (c == '$') || (c == ';') || (c == '-')
          || (c == '_') || (c == '.') || (c == '!') || (c == '~')
          || (c == '*') || (c == ' ') || (c == '(') || (c == ')')) {
        theStringAfterEscape.write(c);
      } else {
        // Add escape
        theStringAfterEscape.write('%');
        String hexString = Integer.toHexString((int) c);
        theStringAfterEscape.write(hexString);
      }
    }
    return theStringAfterEscape.toString();
  }

}
